//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<CDevice10.cpp>
///	@path	~/src/graphics/dx10/
///	@date	2007/09/16
///	@desc	DirectX 10 implementation of IDevice interface.

#include "config/config.h"

#include "lib/utilities/observer.h"
#include "lib/system/thread_debug.h"

#include "graphics/dx10/import_dx10.h"
#include "graphics/dx10/CDevice10.h"
#include "graphics/dx10/enumeration10.h"
#include "graphics/DxHelpers.h"

#include <dxerr.h>
#include <MMSystem.h>

// force linker to link these libs
#pragma comment( linker , "/defaultlib:winmm.lib" )

TRACE_CATEGORY( _S("Graphics/DX10/device") , TRACE_LV_TRACE );

namespace xeres {

	// minimum window size
	static const int DX10_MINWINDOW_SIZE_X = 200;
	static const int DX10_MINWINDOW_SIZE_Y = 200;

	IMPL_IOBJECT_CLASS_DYNAMIC( CDevice10 );

	inline bool CDevice10::is_windowed( CDevice10::_DeviceSettings * deviceSettings )
	{
		if( deviceSettings == NULL )
			return true;
		return deviceSettings->swapChainDesc.Windowed == 1;
	}

	// ctor
	CDevice10::CDevice10( void )
		: m_hWnd( NULL )
		, m_hMenu( NULL )
		, m_hAdapterMonitor( NULL )
		, m_d3dDevice( NULL )
		, m_dxgiFactory( NULL )
		, m_swapChain( NULL )
		, m_adapter( NULL )
		, m_depthStencil( NULL )
		, m_depthStencilView( NULL )
		, m_renderTargetView( NULL )
		, m_rasterizerState( NULL )
		// states
		, m_fullScreenBackBufferWidthAtModeChange( 0 )
		, m_fullScreenBackBufferHeightAtModeChange( 0 )
		, m_windowBackBufferWidthAtModeChange( 0 )
		, m_windowBackBufferHeightAtModeChange( 0 )
		, m_windowedStyleAtModeChange( 0 )
		, m_settingsValid( false )
		, m_ignoreSizeChange( false )
		, m_doNotStoreBufferSize( false )
		, m_noUpdate( false )
		, m_deviceCreated( false )
		, m_releasingSwapChain( false )
		, m_deviceLost( false )
		, m_insideDeviceCallback( false )
		, m_enablePerf( false )
		, m_enableStat( false )
		, m_minimized( false )
		, m_usePerfHud( false )
	{
		memset( &m_currentSettings , 0 , sizeof(_DeviceSettings) );
		memset( &m_backBufferSurfaceDesc , 0 , sizeof(DXGI_SURFACE_DESC) );
		memset( &m_windowedPlacement , 0 , sizeof(WINDOWPLACEMENT) );
	}

	// dtor
	CDevice10::~CDevice10( void )
	{
		if( m_d3dDevice )
		{
			CleanupEnvironment( true );
		}
	}

	// check DirectX 10 version
	bool CDevice10::CheckDeviceVersion( void )
	{
		return true;
	}

	// enable render
	bool CDevice10::IsEnableRender( void )
	{
		RUNNING_ON_THREAD( _S("main|render|!_tick") );

		if( m_noUpdate || m_deviceLost || !m_deviceCreated )
			return false;

		HRESULT hr = m_swapChain->Present( 0 , DXGI_PRESENT_TEST );
		if( FAILED( hr ) )
		{
			if( hr == DXGI_ERROR_DEVICE_RESET || hr == DXGI_ERROR_DEVICE_REMOVED )
			{
				m_deviceLost = true;
				return true;
			}
			else if( hr == DXGI_STATUS_OCCLUDED )
			{
				return false;
			}
			else
			{
				TRACE_ERROR( _S("CDevice10::IsEnableRender: Device is not ready to render: %s.") ,
					d3d_get_err( hr ) );
				return false;
			}
		}
		return true;
	}

	// begin render
	bool CDevice10::BeginRender( void )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("render|!_tick") );

		COUNTER_GUARD( _S("overhead") );

		if( m_d3dDevice == NULL || m_swapChain == NULL )
			return false;

		if( m_deviceLost )
		{
			// Window is minimized or paused so yield CPU time to other processes
			::Sleep( 50 ); 
		}

		HRESULT hr = m_swapChain->Present( 0 , DXGI_PRESENT_TEST );
		if( FAILED( hr ) )
		{
			if( hr == DXGI_ERROR_DEVICE_RESET || hr == DXGI_ERROR_DEVICE_REMOVED )
			{
				m_deviceLost = true;
				return true;
			}
			else if( hr == DXGI_STATUS_OCCLUDED )
			{
				return false;
			}
			else
			{
				TRACE_ERROR( _S("CDevice10::BeginRender: Device is not ready to render: %s.") ,
					d3d_get_err( hr ) );
				return false;
			}
		}

		// Test if device lost, need change mode.
		if( m_d3dDevice == NULL || m_swapChain == NULL )
		{
			if( m_deviceLost )
			{
				_DeviceSettings settings = m_currentSettings;
				ChangeMode( &settings );
				return false;
			}
			return false;
		}

		// Change mode while device lost
		if( m_deviceLost )
		{
			hr = S_OK;
			 // Test the cooperative level to see if it's OK to render.
			if( FAILED( hr = m_swapChain->Present( 0 , DXGI_PRESENT_TEST ) ) )
			{
				if( DXGI_ERROR_DEVICE_RESET == hr || DXGI_ERROR_DEVICE_REMOVED == hr )
				{
					// The device has been lost but cannot be reset at this time.
					// So wait until it can be reset.
					return false;
				}

				// If we are windowed, read the desktop format and 
				// ensure that the Direct3D device is using the same format 
				// since the user could have changed the desktop bitdepth 
				if( m_currentSettings.swapChainDesc.Windowed )
				{
					DXGI_MODE_DESC adapterDesktopDisplayMode;
					_DeviceSettings * pDeviceSettings = &m_currentSettings;
					GetAdapterDisplayMode( pDeviceSettings->adapterOrdinal, pDeviceSettings->output, adapterDesktopDisplayMode );
					if( pDeviceSettings->swapChainDesc.BufferDesc.Format != adapterDesktopDisplayMode.Format )
					{
						_MatchOptions options;
						options.AdapterOrdinal     = MATCH_PRESERVE_INPUT;
						options.Output				= MATCH_PRESERVE_INPUT;
						options.DeviceType         = MATCH_PRESERVE_INPUT;
						options.Windowed           = MATCH_PRESERVE_INPUT;
						options.AdapterFormat      = MATCH_PRESERVE_INPUT;
						options.VertexProcessing   = MATCH_CLOSEST_TO_INPUT;
						options.Resolution         = MATCH_CLOSEST_TO_INPUT;
						options.BackBufferFormat   = MATCH_CLOSEST_TO_INPUT;
						options.BackBufferCount    = MATCH_CLOSEST_TO_INPUT;
						options.MultiSample        = MATCH_CLOSEST_TO_INPUT;
						options.SwapEffect         = MATCH_CLOSEST_TO_INPUT;
						options.DepthFormat        = MATCH_CLOSEST_TO_INPUT;
						options.StencilFormat      = MATCH_CLOSEST_TO_INPUT;
						options.PresentFlags       = MATCH_CLOSEST_TO_INPUT;
						options.RefreshRate        = MATCH_CLOSEST_TO_INPUT;
						options.PresentInterval    = MATCH_CLOSEST_TO_INPUT;

						_DeviceSettings deviceSettings = m_currentSettings;
						deviceSettings.swapChainDesc.BufferDesc.Format = adapterDesktopDisplayMode.Format;
						_DeviceSettings optimal;
						BuildOptimalSettings( &deviceSettings , &optimal , &options );
						// the call will fail if no valid devices were found
						if( FindValidSettings( &deviceSettings, &deviceSettings, &optimal , &options ) )
						{
							TRACE_ERROR( _S("CDevice10::BeginRender: Device is compatible with settings.") );
							FATAL_ABORT( _S("Device is not compatible.") );
						}

						// Change to a Direct3D device created from the new device settings.
						// If there is an existing device, then either reset or recreate the scene
						hr = ChangeMode( &deviceSettings );
						if( FAILED(hr) )  
						{
							TRACE_ERROR( _S("CDevice10::BeginRender: Failed to recreate device while device lost: %s.") ,
								d3d_get_err( hr ) );
							FATAL_ABORT( _S("Failed to change mode while device lost.") );
						}
						return true;
					}
				}

				// Try to reset the device
				if( FAILED( hr = ResetEnvironment() ) )
				{
					if( D3DERR_DEVICELOST == hr )
					{
						// The device was lost again, so continue waiting until it can be reset.
						return false;
					}
					else
					{
						// Reset failed, but the device wasn't lost so something bad happened, 
						// so recreate the device to try to recover
						_DeviceSettings settings = m_currentSettings;
						if( FAILED( ChangeMode( &settings ) ) )
						{
							TRACE_ERROR( _S("CDevice10::BeginRender: Failed to recreate device while device lost: %s.") ,
								d3d_get_err( hr ) );
							FATAL_ABORT( _S("Failed to change mode while device lost.") );
						}
					}
				}
			}

			m_deviceLost = false;
		}
		if( m_deviceLost )
		{
			// Try to reset the device
			if( FAILED( hr = ResetEnvironment() ) )
			{
				if( D3DERR_DEVICELOST == hr )
				{
					// The device was lost again, so continue waiting until it can be reset.
					return false;
				}
				else
				{
					// Reset failed, but the device wasn't lost so something bad happened, 
					// so recreate the device to try to recover
					_DeviceSettings settings = m_currentSettings;
					if( FAILED( ChangeMode( &settings ) ) )
					{
						TRACE_ERROR( _S("CDevice10::BeginRender: Failed to recreate device while device lost: %s.") ,
							d3d_get_err( hr ) );
						FATAL_ABORT( _S("Failed to change mode while device lost.") );
					}
				}
			}
		}

		return true;
	}

	// end render
	void CDevice10::EndRender( void )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("render|!_tick") );

		COUNTER_GUARD( _S("overhead") );

		//assert( !m_noUpdate && !m_deviceLost && m_deviceCreated && !m_minimized );

		// present update
		HRESULT hr = m_swapChain->Present( 0 /*FIXME: use configurated interval*/ , 0 );
		if( FAILED(hr) )
		{
			TRACE_ERROR( _S("CDevice9::EndRender: Failed to present: %s.") , d3d_get_err(hr) );
		}

		//m_d3dDevice->ClearState();
	}

	// ToggleFullScreen
	bool CDevice10::ToggleFullScreen( void * window )
	{
		// must run on device
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("device") );

		// Get the current device settings and flip the windowed state then
		// find the closest valid device settings with this change
		_DeviceSettings deviceSettings = m_currentSettings;
		_DeviceSettings orginalDeviceSettings = m_currentSettings;

		// Toggle windowed/fullscreen bit
		deviceSettings.swapChainDesc.Windowed = !deviceSettings.swapChainDesc.Windowed;

		_MatchOptions options;
		options.AdapterOrdinal     = MATCH_PRESERVE_INPUT;
		options.Output			   = MATCH_PRESERVE_INPUT;
		options.DeviceType         = MATCH_CLOSEST_TO_INPUT;
		options.Windowed           = MATCH_PRESERVE_INPUT;
		options.AdapterFormat      = MATCH_IGNORE_INPUT;
		options.VertexProcessing   = MATCH_CLOSEST_TO_INPUT;
		options.BackBufferFormat   = MATCH_IGNORE_INPUT;
		options.BackBufferCount    = MATCH_CLOSEST_TO_INPUT;
		options.MultiSample        = MATCH_CLOSEST_TO_INPUT;
		options.SwapEffect         = MATCH_CLOSEST_TO_INPUT;
		options.DepthFormat        = MATCH_CLOSEST_TO_INPUT;
		options.StencilFormat      = MATCH_CLOSEST_TO_INPUT;
		options.PresentFlags       = MATCH_CLOSEST_TO_INPUT;
		options.RefreshRate        = MATCH_IGNORE_INPUT;
		options.PresentInterval    = MATCH_CLOSEST_TO_INPUT;

		// Go back to previous state

		BOOL bIsWindowed = is_windowed( &deviceSettings );
		UINT nWidth  = ( bIsWindowed ) ?
			m_windowBackBufferWidthAtModeChange : m_fullScreenBackBufferWidthAtModeChange;
		UINT nHeight = ( bIsWindowed ) ?
			m_windowBackBufferHeightAtModeChange : m_fullScreenBackBufferHeightAtModeChange;

		if( nWidth > 0 && nHeight > 0 )
		{
			options.Resolution = MATCH_CLOSEST_TO_INPUT;

			deviceSettings.swapChainDesc.BufferDesc.Width = nWidth;
			deviceSettings.swapChainDesc.BufferDesc.Height = nHeight;
		}
		else
		{
			// No previous data, so just switch to defaults
			options.Resolution = MATCH_IGNORE_INPUT;
		}

		_DeviceSettings optimal;
		BuildOptimalSettings( &deviceSettings , &optimal , &options );
		if( FindValidSettings( &deviceSettings , &deviceSettings , &optimal , &options ) ) 
		{
			// Create a Direct3D device using the new device settings.  
			// If there is an existing device, then it will either reset or recreate the scene.
			HRESULT hr = ChangeMode( &deviceSettings );

			// If hr == E_ABORT, this means the app rejected the device settings in the ModifySettingsCallback so nothing changed
			if( FAILED(hr) && (hr != E_ABORT) )
			{
				// Failed creating device, try to switch back.
				HRESULT hr2 = ChangeMode( &orginalDeviceSettings );
				if( FAILED(hr2) )
				{
					// If this failed, then shutdown
					TRACE_ERROR( _S("CDevice10::ToggleFullScreen: Failed to toggle full screen: %s.") ,
						d3d_get_err( hr2 ) );
					FATAL_ABORT( _S("Failed to toggle full screen.") );
				}
			}
		}
		return true;
	}

	// IsFullScreen
	bool CDevice10::IsFullScreen( void )
	{
		return !is_windowed( &m_currentSettings );
	}

	// attach device callback
	void CDevice10::AttachCallback( RefWeak<IDeviceCallback> callback )
	{
		// all callback changes should be synchronized
		RUNNING_ON_THREAD( _S("main") );

		// lock operation
		LockGuard<ThreadLock> __lockGuard( m_lock );
		m_callbacks.push_back( callback );
	}

	// determine if device can be reset
	bool CDevice10::CanDeviceBeReset( _DeviceSettings * oldSettings , _DeviceSettings * newSettings )
	{
		if( oldSettings == NULL )
			return false;
		return 
			( oldSettings->adapterOrdinal == newSettings->adapterOrdinal ) &&
            ( oldSettings->driverType == newSettings->driverType ) &&
            ( oldSettings->createFlags == newSettings->createFlags ) &&
            ( oldSettings->swapChainDesc.SampleDesc.Count == newSettings->swapChainDesc.SampleDesc.Count ) &&
            ( oldSettings->swapChainDesc.SampleDesc.Quality == newSettings->swapChainDesc.SampleDesc.Quality );
	}

	// change device
	HRESULT CDevice10::ChangeMode( _DeviceSettings * newSettings )
	{
		TRACK_FUNCTION_SCOPE();
		HRESULT hr = S_OK;

		TRACE_INFO( _S("Change DirectX driver mode.") );
		// change device

		_DeviceSettings * oldSettings = m_settingsValid ? &m_currentSettings : NULL;

		// invoke ChangeModePreEvent to all registered callbacks
		m_insideDeviceCallback = true;
		for( size_t i = 0 , total = m_callbacks.size() ; i < total ; ++i )
		{
			bool res = m_callbacks[i]->PreChangeMode( this , oldSettings , newSettings );
			// handle result
			if( !res )
			{
				FATAL_ABORT( _S("Device pre-change mode callback failed.") );
			}
		}
		m_insideDeviceCallback = false;

		// pause device loop (pause update loop)
		m_noUpdate = true;

		// omit size change events while changing mode
		m_ignoreSizeChange = true;

		// FIXME: override specific settings from config

		// check if we need to change window size
		bool bKeepCurrentWindowSize = 
			( newSettings->swapChainDesc.BufferDesc.Width == 0 ) &&
			( newSettings->swapChainDesc.BufferDesc.Height == 0 );

		// -------------------------------
		// Before we reset device
		// -------------------------------

		// check windowed
		if( is_windowed( newSettings ) )
		{
			if( oldSettings && !is_windowed( oldSettings ) )
			{
				TRACE_INFO( _S("Enter windowed mode.") );
				// change from fullscreen to windowed
				m_fullScreenBackBufferWidthAtModeChange =
					oldSettings->swapChainDesc.BufferDesc.Width;
				m_fullScreenBackBufferHeightAtModeChange =
					oldSettings->swapChainDesc.BufferDesc.Height;

				// Restore windowed mode style
				:: SetWindowLong( m_hWnd , GWL_STYLE , m_windowedStyleAtModeChange );
			}

			// Reattach menu if one exists
			if( m_hMenu != NULL )
				::SetMenu( m_hWnd , m_hMenu );
		}
		else
		{
			// change to fullscreen mode
			if( oldSettings == NULL || ( oldSettings && is_windowed( oldSettings ) ) )
			{
				TRACE_INFO( _S("Enter full-screen mode.") );
				// Transitioning to full screen mode from a standard window so 
				// save current window position/size/style now in case the user toggles to windowed mode later 
				DWORD dwStyle = ::GetWindowLong( m_hWnd , GWL_STYLE );
				dwStyle &= ~WS_MAXIMIZE & ~WS_MINIMIZE; // remove minimize/maximize style
				m_windowedStyleAtModeChange = dwStyle;
                if( oldSettings )
                {
                    m_windowBackBufferWidthAtModeChange =
						oldSettings->swapChainDesc.BufferDesc.Width;
                    m_windowBackBufferHeightAtModeChange =
						oldSettings->swapChainDesc.BufferDesc.Height;
                }
			}

			// Hide the window to avoid animation of blank windows
			::ShowWindow( m_hWnd , SW_HIDE );

			// Set FS window style
			::SetWindowLong( m_hWnd , GWL_STYLE, WS_POPUP|WS_SYSMENU );

			// save and remove menu 
			m_hMenu = ::GetMenu( m_hWnd );
			::SetMenu( m_hWnd , NULL );

			WINDOWPLACEMENT wpFullscreen;
			::ZeroMemory( &wpFullscreen, sizeof(WINDOWPLACEMENT) );
			wpFullscreen.length = sizeof(WINDOWPLACEMENT);
			::GetWindowPlacement( m_hWnd , &wpFullscreen );
			if( (wpFullscreen.flags & WPF_RESTORETOMAXIMIZED) != 0 )
			{
				// Restore the window to normal if the window was maximized then minimized.  This causes the 
				// WPF_RESTORETOMAXIMIZED flag to be set which will cause SW_RESTORE to restore the 
				// window from minimized to maximized which isn't what we want
				wpFullscreen.flags &= ~WPF_RESTORETOMAXIMIZED;
				wpFullscreen.showCmd = SW_RESTORE;
				::SetWindowPlacement( m_hWnd, &wpFullscreen );
			}
		}

		// reset device & create device
		if( CanDeviceBeReset( oldSettings , newSettings ) )
		{
			hr = ResetEnvironment();

			if( FAILED(hr) )
			{
				if( D3DERR_DEVICELOST == hr )
				{
					// The device is lost, just mark it as so and continue on with 
					// capturing the state and resizing the window/etc.
					m_deviceLost = true;
				}
			}
		}
		else
		{
			// Cleanup if not first device created
			if( oldSettings ) 
				CleanupEnvironment( false );

			// Create the D3D device and call the app's device callbacks

			memcpy( &m_currentSettings , newSettings , sizeof(_DeviceSettings) );
			hr = CreateEnvironment();

			if( FAILED(hr) )
			{
				CleanupEnvironment( true );
				// ! FIXME ! trace fatal error!
				return hr;
			}
		}

		// m_hAdapterMonitor = GetMonitorFromAdapter();

		// FIXME: update frame statistics

		// Restore window placement
		if( oldSettings && ! is_windowed( oldSettings ) && is_windowed( newSettings ) )
		{
			// Going from fullscreen -> windowed

			// Restore the show state, and positions/size of the window to what it was
			// It is important to adjust the window size 
			// after resetting the device rather than beforehand to ensure 
			// that the monitor resolution is correct and does not limit the size of the new window.
			::SetWindowPlacement( m_hWnd , &m_windowedPlacement );

			// Also restore the z-order of window to previous state
			HWND hWndInsertAfter = /* etTopmostWhileWindowed() ? HWND_TOPMOST : */ HWND_NOTOPMOST;
			::SetWindowPos( m_hWnd , hWndInsertAfter , 0 , 0 , 0 , 0 , SWP_NOMOVE|SWP_NOREDRAW|SWP_NOSIZE );
		}

		// Check to see if the window needs to be resized.  
		// Handle cases where the window is minimized and maxmimized as well.
		bool bNeedToResize = false;
		if( is_windowed( newSettings ) &&	// only resize if in windowed mode
			!bKeepCurrentWindowSize )		// only resize if pp.BackbufferWidth/Height were not 0
		{
			UINT nClientWidth;
			UINT nClientHeight;    
			if( ::IsIconic( m_hWnd ) )
			{
				// Window is currently minimized. To tell if it needs to resize, 
				// get the client rect of window when its restored the 
				// hard way using GetWindowPlacement()
				WINDOWPLACEMENT wp;
				memset( &wp , 0 , sizeof(WINDOWPLACEMENT) );
				wp.length = sizeof(WINDOWPLACEMENT);
				::GetWindowPlacement( m_hWnd , &wp );

				if( ( wp.flags & WPF_RESTORETOMAXIMIZED ) != 0 && wp.showCmd == SW_SHOWMINIMIZED )
				{
					// WPF_RESTORETOMAXIMIZED means that when the window is restored it will
					// be maximized.  So maximize the window temporarily to get the client rect 
					// when the window is maximized.  GetSystemMetrics( SM_CXMAXIMIZED ) will give this 
					// information if the window is on the primary but this will work on multimon.
					::ShowWindow( m_hWnd , SW_RESTORE );
					RECT rcClient;
					::GetClientRect( m_hWnd , &rcClient );
					nClientWidth  = (UINT)(rcClient.right - rcClient.left);
					nClientHeight = (UINT)(rcClient.bottom - rcClient.top);
					::ShowWindow( m_hWnd , SW_MINIMIZE );
				}
				else
				{
					// Use wp.rcNormalPosition to get the client rect, but wp.rcNormalPosition 
					// includes the window frame so subtract it
					RECT rcFrame = {0};
					::AdjustWindowRect( &rcFrame, m_windowedStyleAtModeChange , FALSE );
					LONG nFrameWidth = rcFrame.right - rcFrame.left;
					LONG nFrameHeight = rcFrame.bottom - rcFrame.top;
					nClientWidth  = (UINT)(wp.rcNormalPosition.right - wp.rcNormalPosition.left - nFrameWidth);
					nClientHeight = (UINT)(wp.rcNormalPosition.bottom - wp.rcNormalPosition.top - nFrameHeight);
				}
			}
			else
			{
				// Window is restored or maximized so just get its client rect
				RECT rcClient;
				::GetClientRect( m_hWnd, &rcClient );
				nClientWidth  = (UINT)(rcClient.right - rcClient.left);
				nClientHeight = (UINT)(rcClient.bottom - rcClient.top);
			}

			// Now that we know the client rect, compare it against the back buffer size
			// to see if the client rect is already the right size
			if( nClientWidth  != newSettings->swapChainDesc.BufferDesc.Width ||
				nClientHeight != newSettings->swapChainDesc.BufferDesc.Height )
			{
				bNeedToResize = true;
			}
		}

		// Only resize window if needed 
		if( bNeedToResize ) 
		{
			// Need to resize, so if window is maximized or minimized then restore the window
			if( ::IsIconic( m_hWnd ) ) 
				::ShowWindow( m_hWnd , SW_RESTORE );
			if( ::IsZoomed( m_hWnd ) ) // doing the IsIconic() check first also handles the WPF_RESTORETOMAXIMIZED case
				::ShowWindow( m_hWnd , SW_RESTORE );


			// Make a window rect with a client rect that is the same size as the backbuffer
			RECT rcWindow = {0};
			rcWindow.right = (long)( newSettings->swapChainDesc.BufferDesc.Width );
			rcWindow.bottom = (long)( newSettings->swapChainDesc.BufferDesc.Height );
			::AdjustWindowRect( &rcWindow , ::GetWindowLong( m_hWnd , GWL_STYLE ) , FALSE );

			// Resize the window.  It is important to adjust the window size 
			// after resetting the device rather than beforehand to ensure 
			// that the monitor resolution is correct and does not limit the size of the new window.
			int cx = (int)(rcWindow.right - rcWindow.left);
			int cy = (int)(rcWindow.bottom - rcWindow.top);
			::SetWindowPos( m_hWnd , 0 , 0 , 0 , cx , cy , SWP_NOZORDER|SWP_NOMOVE );

			// Its possible that the new window size is not what we asked for.  
			// No window can be sized larger than the desktop, so see if the Windows OS resized the 
			// window to something smaller to fit on the desktop.  Also if WM_GETMINMAXINFO
			// will put a limit on the smallest/largest window size.
			RECT rcClient;
			::GetClientRect( m_hWnd , &rcClient );
			UINT nClientWidth  = (UINT)(rcClient.right - rcClient.left);
			UINT nClientHeight = (UINT)(rcClient.bottom - rcClient.top);
			if( nClientWidth  != newSettings->swapChainDesc.BufferDesc.Width  ||
				nClientHeight != newSettings->swapChainDesc.BufferDesc.Height )
			{
				// If its different, then resize the backbuffer again.  This time create a backbuffer that matches the 
				// client rect of the current window w/o resizing the window.
				_DeviceSettings deviceSettings = m_currentSettings;
				deviceSettings.swapChainDesc.BufferDesc.Width = 0; 
				deviceSettings.swapChainDesc.BufferDesc.Height = 0;

				hr = ChangeMode( &deviceSettings );
				if( FAILED( hr ) )
				{
					CleanupEnvironment( true );
					m_ignoreSizeChange = false;
					return hr;
				}
			}
		}

		// Make the window visible
		if( !::IsWindowVisible( m_hWnd ) )
			::ShowWindow( m_hWnd , SW_SHOW );

		// Ensure that the display doesn't power down when fullscreen but does when windowed
		if( !is_windowed( newSettings ) )
			::SetThreadExecutionState( ES_DISPLAY_REQUIRED | ES_CONTINUOUS ); 
		else
			::SetThreadExecutionState( ES_CONTINUOUS );   

		// invoke ChangeModePostEvent to all registered callbacks
		m_insideDeviceCallback = true;
		for( size_t i = 0 , total = m_callbacks.size() ; i < total ; ++i )
		{
			bool res = m_callbacks[i]->PostChangeMode( this , &m_currentSettings );
			// handle result
			if( !res )
			{
				FATAL_ABORT( _S("Device post change mode callback failed.") );
			}
		}
		m_insideDeviceCallback = false;

		// restore states
		m_ignoreSizeChange = false;
		m_deviceCreated = true;
		m_settingsValid = true;
		m_noUpdate = false;

		return S_OK;
	}

	// create 3d environment
	HRESULT CDevice10::CreateEnvironment( void )
	{
		TRACK_FUNCTION_SCOPE();
		HRESULT hr = S_OK;

		// stop DXGI from monitoring alt+enter key
		hr = m_dxgiFactory->MakeWindowAssociation( NULL, 0 );

		if( m_d3dDevice == NULL )
		{
			// Try to create the device with the chosen settings
			hr = S_OK;
			if( m_currentSettings.driverType == D3D10_DRIVER_TYPE_HARDWARE )
			{
				hr = m_dxgiFactory->EnumAdapters( m_currentSettings.adapterOrdinal, &m_adapter );
			}

			if( m_enablePerf )
			{
				UINT nAdapter = 0;
				IDXGIAdapter * adapter = NULL;
				while( m_dxgiFactory->EnumAdapters( nAdapter , &adapter ) != DXGI_ERROR_NOT_FOUND )
				{
					if( adapter )
					{
						DXGI_ADAPTER_DESC adaptDesc;
						if( SUCCEEDED( adapter->GetDesc( &adaptDesc ) ) )
						{
							const bool isPerfHUD = wcscmp( adaptDesc.Description, L"NVIDIA PerfHUD" ) == 0;
							if( isPerfHUD )
							{
								if( m_adapter )
									m_adapter->Release();
								m_adapter = adapter;
								m_currentSettings.adapterOrdinal = nAdapter;
								m_currentSettings.driverType = D3D10_DRIVER_TYPE_REFERENCE;
								m_usePerfHud = true;
								break;
							}
						}
						else
						{
							adapter->Release();
							adapter = NULL;
						}
					}
					++nAdapter;
				}
			}

			TRACE_INFO( _S("Creating Direct3D 10 device...") );

			if( SUCCEEDED(hr) )
			{
				hr = dx10::D3D10CreateDevice( m_adapter , m_currentSettings.driverType ,
					NULL , m_currentSettings.createFlags , D3D10_SDK_VERSION, &m_d3dDevice );

				if( SUCCEEDED(hr) )
				{
					if( m_currentSettings.driverType != D3D10_DRIVER_TYPE_HARDWARE )
					{
						IDXGIDevice* pDXGIDev = NULL;
						hr = m_d3dDevice->QueryInterface( IID_IDXGIDevice , (LPVOID*)( &pDXGIDev ) );
						if( SUCCEEDED(hr) && pDXGIDev )
						{
							if( m_adapter )
								m_adapter->Release();
							pDXGIDev->GetAdapter( &m_adapter );
						}
						if( pDXGIDev )
							pDXGIDev->Release();
					}
				}
			}

			if( FAILED(hr) )
			{
				TRACE_ERROR( _S("CDevice10::CreateEnvironment: Failed to create device : %s.") ,
					d3d_get_err(hr) );
				return hr;
			}

			// enumerate outputs
			size_t output_count;
			for( output_count = 0 ; ; ++output_count )
			{
				IDXGIOutput* pOutput;
				if( FAILED( m_adapter->EnumOutputs( (UINT)output_count, &pOutput ) ) )
					break;
				if( pOutput )
					pOutput->Release();
			}
			m_outputs.resize( output_count );
			for( size_t i = 0 ; i < output_count ; ++i )
			{
				m_adapter->EnumOutputs( (UINT)i , &( m_outputs[i] ) );
			}

			TRACE_INFO( _S("Creating Direct3D swap chain...") );

			// create swap chain
			hr = m_dxgiFactory->CreateSwapChain( m_d3dDevice , &(m_currentSettings.swapChainDesc) , &m_swapChain );
			if( FAILED(hr) )
			{
				TRACE_ERROR( _S("CDevice10::createEnvironment: Failed to create swap chain : %s.") ,
					d3d_get_err(hr) );
				return hr;
			}
		}
		else
		{
			// do nothing.
		}

		// Update back buffer desc before calling app's device callbacks
		UpdateBackBufferDesc();

		// Update the device stats text
		EnumD3D10 * pd3dEnum = &EnumD3D10::instance();
		const EnumAdapterInfo10 * pAdapterInfo = pd3dEnum->GetAdapterInfo( m_currentSettings.adapterOrdinal );

		// Setup views.
		hr = SetupViews();
		if( FAILED(hr) )
		{
			TRACE_ERROR( _S("CDevice10::createEnvironment: Failed to setup views : %s.") ,
				d3d_get_err(hr) );
			return hr;
		}

		// call reset device callback
		m_insideDeviceCallback = true;
		for( size_t i = 0 , total = m_callbacks.size() ; i < total ; ++i )
		{	
			bool res = m_callbacks[i]->OnResetDevice( this  );
			// handle result
			if( !res )
			{
				// FIXME: trace error
				FATAL_ABORT( _S("Device reset device callback failed.") );
			}
		}
		m_insideDeviceCallback = false;

		return S_OK;
	}

	// SetupViews
	HRESULT CDevice10::SetupViews( void )
	{
		HRESULT hr = S_OK;
		ID3D10DepthStencilView* pDSV = NULL;
		ID3D10RenderTargetView* pRTV = NULL;

		// Get the back buffer and desc
		ID3D10Texture2D * pBackBuffer;
		hr = m_swapChain->GetBuffer( 0, IID_ID3D10Texture2D , ( LPVOID* )&pBackBuffer );
		if( FAILED( hr ) )
			return hr;

		D3D10_TEXTURE2D_DESC backBufferSurfaceDesc;
		pBackBuffer->GetDesc( &backBufferSurfaceDesc );

		// Setup the viewport to match the backbuffer
		D3D10_VIEWPORT vp;
		vp.Width = backBufferSurfaceDesc.Width;
		vp.Height = backBufferSurfaceDesc.Height;
		vp.MinDepth = 0;
		vp.MaxDepth = 1;
		vp.TopLeftX = 0;
		vp.TopLeftY = 0;
		m_d3dDevice->RSSetViewports( 1, &vp );

		// Create the render target view
		hr = m_d3dDevice->CreateRenderTargetView( pBackBuffer, NULL, &pRTV );
		pBackBuffer->Release();
		pBackBuffer = NULL;
		if( FAILED( hr ) )
			return hr;

		if( m_renderTargetView )
			m_renderTargetView->Release();
		m_renderTargetView = pRTV;

		if( m_currentSettings.autoCreateDepthStencil )
		{
			// Create depth stencil texture
			ID3D10Texture2D * pDepthStencil = NULL;
			D3D10_TEXTURE2D_DESC descDepth;
			descDepth.Width = backBufferSurfaceDesc.Width;
			descDepth.Height = backBufferSurfaceDesc.Height;
			descDepth.MipLevels = 1;
			descDepth.ArraySize = 1;
			descDepth.Format = m_currentSettings.autoDepthStencilFormat;
			descDepth.SampleDesc.Count = m_currentSettings.swapChainDesc.SampleDesc.Count;
			descDepth.SampleDesc.Quality = m_currentSettings.swapChainDesc.SampleDesc.Quality;
			descDepth.Usage = D3D10_USAGE_DEFAULT;
			descDepth.BindFlags = D3D10_BIND_DEPTH_STENCIL;
			descDepth.CPUAccessFlags = 0;
			descDepth.MiscFlags = 0;
			hr = m_d3dDevice->CreateTexture2D( &descDepth, NULL, &pDepthStencil );
			if( FAILED( hr ) )
				return hr;

			// Create the depth stencil view
			D3D10_DEPTH_STENCIL_VIEW_DESC descDSV;
			descDSV.Format = descDepth.Format;
			if( descDepth.SampleDesc.Count > 1 )
				descDSV.ViewDimension = D3D10_DSV_DIMENSION_TEXTURE2DMS;
			else
				descDSV.ViewDimension = D3D10_DSV_DIMENSION_TEXTURE2D;
			descDSV.Texture2D.MipSlice = 0;
			hr = m_d3dDevice->CreateDepthStencilView( pDepthStencil, &descDSV, &pDSV );
			if( FAILED( hr ) )
				return hr;

			m_depthStencilView = pDSV;
			m_depthStencil = pDepthStencil;

			// Create a default rasterizer state that enables MSAA
			D3D10_RASTERIZER_DESC RSDesc;
			RSDesc.FillMode = D3D10_FILL_SOLID;
			RSDesc.CullMode = D3D10_CULL_NONE;
			RSDesc.FrontCounterClockwise = FALSE;
			RSDesc.DepthBias = 0;
			RSDesc.SlopeScaledDepthBias = 0.0f;
			RSDesc.DepthBias = 0;
			RSDesc.DepthClipEnable = FALSE;
			RSDesc.ScissorEnable = FALSE;
			RSDesc.AntialiasedLineEnable = FALSE;
			if( descDepth.SampleDesc.Count > 1 )
				RSDesc.MultisampleEnable = TRUE;
			else
				RSDesc.MultisampleEnable = FALSE;

			ID3D10RasterizerState * pRState = NULL;
			hr = m_d3dDevice->CreateRasterizerState( &RSDesc, &pRState );
			if( FAILED( hr ) )
				return hr;

			m_rasterizerState = pRState;
			m_d3dDevice->RSSetState( pRState );
		}

		// Set the render targets
		pDSV = m_depthStencilView;
		m_d3dDevice->OMSetRenderTargets( 1, &pRTV, pDSV );

		return hr;
	}

	// update back buffer desc
	void CDevice10::UpdateBackBufferDesc( void )
	{
		ID3D10Texture2D * pBackBuffer;
		HRESULT hr = m_swapChain->GetBuffer( 0 , IID_ID3D10Texture2D , (void **)&pBackBuffer );
		memset( &m_backBufferSurfaceDesc , 0 , sizeof(DXGI_SURFACE_DESC) );
		if( SUCCEEDED(hr) )
		{
			D3D10_TEXTURE2D_DESC TexDesc;
			pBackBuffer->GetDesc( &TexDesc );
			m_backBufferSurfaceDesc.Width = (UINT) TexDesc.Width;
			m_backBufferSurfaceDesc.Height = (UINT) TexDesc.Height;
			m_backBufferSurfaceDesc.Format = TexDesc.Format;
			m_backBufferSurfaceDesc.SampleDesc = TexDesc.SampleDesc;
			pBackBuffer->Release();
			pBackBuffer = NULL;
		}
	}

	// reset device
	HRESULT CDevice10::ResetEnvironment( void )
	{
		HRESULT hr;

		DXGI_SWAP_CHAIN_DESC SCDesc;
		m_swapChain->GetDesc( &SCDesc );

		// Resize backbuffer and target of the swapchain in case they have changed.
		// For windowed mode, use the client rect as the desired size. Unlike D3D9,
		// we can't use 0 for width or height.  Therefore, fill in the values from
		// the window size. For fullscreen mode, the width and height should have
		// already been filled with the desktop resolution, so don't change it.
		if( m_currentSettings.swapChainDesc.Windowed && SCDesc.Windowed )
		{
			RECT rcWnd;
			::GetClientRect( m_hWnd , &rcWnd );
			m_currentSettings.swapChainDesc.BufferDesc.Width = rcWnd.right - rcWnd.left;
			m_currentSettings.swapChainDesc.BufferDesc.Height = rcWnd.bottom - rcWnd.top;
		}

		// If the app wants to switch from windowed to fullscreen or vice versa,
		// call the swapchain's SetFullscreenState
		// mode.
		if( SCDesc.Windowed != m_currentSettings.swapChainDesc.Windowed )
		{
			// Set the fullscreen state
			if( m_currentSettings.swapChainDesc.Windowed )
			{
				if( FAILED( hr = m_swapChain->SetFullscreenState( FALSE, NULL ) ) )
					return hr;
			}
			else
			{
				// Set fullscreen state by setting the display mode to fullscreen, then changing the resolution
				// to the desired value.

				// SetFullscreenState causes a WM_SIZE message to be sent to the window.  The WM_SIZE message calls
				// DXUTCheckForDXGIBufferChange which normally stores the new height and width in 
				// pDeviceSettings->d3d10.sd.BufferDesc.  SetDoNotStoreBufferSize tells DXUTCheckForDXGIBufferChange
				// not to store the height and width so that we have the correct values when calling ResizeTarget.
				m_doNotStoreBufferSize = true;
				if( FAILED( hr = m_swapChain->SetFullscreenState( TRUE, NULL ) ) )
					return hr;
				m_doNotStoreBufferSize = false;
				if( FAILED( hr = m_swapChain->ResizeTarget( &m_currentSettings.swapChainDesc.BufferDesc ) ) )
					return hr;
			}
		}
		else
		{
			if( FAILED( hr = m_swapChain->ResizeTarget( &m_currentSettings.swapChainDesc.BufferDesc ) ) )
				return hr;
		}

		// If no deferred DXGI actions are to take place, mark the device as reset.
		// If there is a deferred DXGI action, then the device isn't reset until DXGI sends us a 
		// window message.  Only then can we mark the device as reset.
// 		if( !bDeferredDXGIAction )
// 			GetDXUTState().SetDeviceObjectsReset( true );
// 		DXUTPause( false, false );

		return S_OK;
	}

	// cleanup environment
	void CDevice10::CleanupEnvironment( bool resetSettings )
	{
		if( m_d3dDevice == NULL )
			return;

		// Call ClearState to avoid tons of messy debug spew telling us that we're deleting bound objects
        m_d3dDevice->ClearState();

		m_insideDeviceCallback = true;
		for( size_t i = 0 , total = m_callbacks.size() ; i < total ; ++i )
		{
			bool res = m_callbacks[i]->OnResizingSwapchain( this );
			// handle result
			if( !res )
			{
				FATAL_ABORT( _S("Device resizing swap chain callback failed.") );
			}
		}
		m_insideDeviceCallback = false;

		// Release our old depth stencil texture and view 
		if( m_depthStencil )
		{
			UINT rest = m_depthStencil->Release();
			if( rest > 0 )
            {
				TRACE_ERROR( _S("Invalid Depth stencil Interface reference counter (%u).") , rest );
				FATAL_ABORT( _S("Invalid Depth stencil Interface reference counter.") );
            }
			m_depthStencil = NULL;
		}
		if( m_depthStencilView )
		{
			UINT rest = m_depthStencilView->Release();
			if( rest > 0 )
            {
				TRACE_ERROR( _S("Invalid Depth stencil view Interface reference counter (%u).") , rest );
				FATAL_ABORT( _S("Invalid Depth stencil view Interface reference counter.") );
            }
			m_depthStencilView = NULL;
		}

		// Release our old render target view
		if( m_renderTargetView )
		{
			UINT rest = m_renderTargetView->Release();
			if( rest > 0 )
            {
				TRACE_ERROR( _S("Invalid Render target view Interface reference counter (%u).") , rest );
				FATAL_ABORT( _S("Invalid Render target view Interface reference counter.") );
            }
			m_renderTargetView = NULL;
		}

		// Release our rasterizer state
		if( m_rasterizerState )
		{
			UINT rest = m_rasterizerState->Release();
			if( rest > 0 )
            {
				TRACE_ERROR( _S("Invalid Rasterizer state Interface reference counter (%u).") , rest );
				FATAL_ABORT( _S("Invalid Rasterizer state Interface reference counter.") );
            }
			m_rasterizerState = NULL;
		}

		m_insideDeviceCallback = true;
		for( size_t i = 0 , total = m_callbacks.size() ; i < total ; ++i )
		{
			// Call the app's device lost callback
			bool res = m_callbacks[i]->OnLostDevice( this );
			// handle result
			if( !res )
			{
				FATAL_ABORT( _S("Device lost device callback failed.") );
			}
			// Call the app's device destroyed callback
			res = m_callbacks[i]->OnDestroyDevice( this );
			// handle result
			if( !res )
			{
				FATAL_ABORT( _S("Device destroy device callback failed.") );
			}
		}
		m_insideDeviceCallback = false;

        // Release the swap chain
        if( m_swapChain )
        {
            m_swapChain->SetFullscreenState( FALSE, 0 );
			UINT rest = m_swapChain->Release();
			if( rest > 0 )
            {
				TRACE_ERROR( _S("Invalid IDXGI swap chain Interface reference counter (%u).") , rest );
				FATAL_ABORT( _S("Invalid IDXGI swap chain Interface reference counter.") );
            }
			m_swapChain = NULL;
        }
        m_releasingSwapChain = false;

        // Release the outputs.
		for( size_t i = 0 , total = m_outputs.size() ; i < total ; ++i )
		{
			UINT rest = m_outputs[i]->Release();
			if( rest > 0 )
            {
				TRACE_ERROR( _S("Invalid IDXGI output Interface reference counter (%u).") , rest );
				FATAL_ABORT( _S("Invalid IDXGI output Interface reference counter.") );
            }
		}
		m_outputs.clear();

		m_d3dDevice->ClearState();

        // Release the counters
        //DXUTDestroyD3D10Counters();

        // Release the D3D device and in debug configs, displays a message box if there 
        // are unrelease objects.
        if( m_d3dDevice )
        {
            UINT references = m_d3dDevice->Release();
            if( references > 0 )
            {
				if( !m_usePerfHud )
				{
					TRACE_ERROR( _S("Invalid Direct3D 10 Device Interface reference counter (%u).") , references );
					FATAL_ABORT( _S("Invalid Direct3D 10 Device Interface reference counter.") );
				}
				else
				{
					for( UINT i = 0 ; i < references ; ++i )
						m_d3dDevice->Release();
				}
            }
        }
        m_d3dDevice = NULL;
		m_usePerfHud = false;

		// Release the D3D adapter.
		if( m_adapter )
		{
			UINT rest = m_adapter->Release();
			if( rest > 0 )
            {
				TRACE_ERROR( _S("Invalid IDXGI adapter Interface reference counter (%u).") , rest );
				FATAL_ABORT( _S("Invalid IDXGI adapter Interface reference counter.") );
            }
			m_adapter = NULL;
		}

        if( resetSettings )
        {
            memset( &m_currentSettings , 0 , sizeof(_DeviceSettings) );
			m_settingsValid = false;
        }
		memset( &m_backBufferSurfaceDesc , 0 , sizeof(DXGI_SURFACE_DESC) );
        m_deviceCreated = false;
	}

	// get monitor handle
	HMONITOR CDevice10::GetMonitorFromAdapter( void )
	{
		const EnumOutputInfo10 * pOutputInfo =
			EnumD3D10::instance().GetOutputInfo( m_currentSettings.adapterOrdinal, m_currentSettings.output );
        if( !pOutputInfo )
            return NULL;
		return ::MonitorFromRect( &pOutputInfo->m_desc.DesktopCoordinates, MONITOR_DEFAULTTONEAREST );
	}

	// initialization
	bool CDevice10::Initialize( void * window , RefWeak<IData> config )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("device") );

		HRESULT hr = S_OK;

		// override NV Perf HUD feature
		if( config.IsValid() )
			m_enablePerf = config->ReadBool( _S("device/perf") , false );

		EnumD3D10::instance().Initialize( false );

		// lock operation
		LockGuard<ThreadLock> __lockGuard( m_lock );
		
		// check if initialized
		if( m_d3dDevice != NULL )
		{
			TRACE_ERROR( _S("CDevice10::initialize: Device initialized.") );
			return false;
		}

		// start to perform initialization
		TRACE_INFO( _S("Initializing DirectX 10 device...") );		

		// check DirectX 10 device compatibility
		if( !CheckDeviceVersion() )
		{
			TRACE_ERROR( _S("CDevice10::initialize: DirectX 10 is not installed on the system.") );
			return false;
		}

		// create DXGI factory
		hr = dx10::CreateDXGIFactory( IID_IDXGIFactory , (void**)&m_dxgiFactory );
		if( FAILED( hr ) )
		{
			TRACE_ERROR( _S("CDevice10::initialize: Failed to create XGI Factory.") );
			return false;
		}

		m_hWnd = (HWND)window;

		// FIXME: we ignore config currently, just perform default initialization

		_DeviceSettings settings;
		memset( &settings , 0 , sizeof(_DeviceSettings) );

		// FIXME: should create settings from configuration
		settings.createFlags = 0;
		settings.adapterOrdinal = 0;
		settings.driverType = D3D10_DRIVER_TYPE_HARDWARE;
		settings.output = 0;
		settings.presentFlags = 0;
		settings.syncInterval = 0;
		settings.autoCreateDepthStencil = TRUE;
		settings.autoDepthStencilFormat = DXGI_FORMAT_D32_FLOAT;
		// swap chain desc
		settings.swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
		settings.swapChainDesc.BufferDesc.Width = 800;
		settings.swapChainDesc.BufferDesc.Height = 600;
		settings.swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
		settings.swapChainDesc.BufferDesc.RefreshRate.Numerator = 0;
		settings.swapChainDesc.BufferDesc.RefreshRate.Denominator = 0;
		settings.swapChainDesc.BufferCount = 1;
		settings.swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
		settings.swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
		settings.swapChainDesc.Windowed = TRUE;
		settings.swapChainDesc.OutputWindow = m_hWnd;
		settings.swapChainDesc.SampleDesc.Count = 1;
		settings.swapChainDesc.SampleDesc.Quality = 0;

		// get config from settings
		if( config.IsValid() )
		{
			settings.swapChainDesc.BufferDesc.Width		= config->ReadInt( _S("window/width") , 800 );
			settings.swapChainDesc.BufferDesc.Height	= config->ReadInt( _S("window/height") , 600 );
			settings.swapChainDesc.Windowed				= config->ReadBool( _S("window/fullscreen") , false ) ? FALSE : TRUE ;
			settings.swapChainDesc.BufferCount			= config->ReadBool( _S("device/triple_buffer") , false ) ? 2 : 1;

			m_enableStat = config->ReadBool( _S("device/statistic") , false );
		}


		// select proper device
		_MatchOptions options;
        options.AdapterOrdinal     = MATCH_CLOSEST_TO_INPUT;
		options.Output	           = MATCH_CLOSEST_TO_INPUT;
        options.DeviceType         = MATCH_CLOSEST_TO_INPUT;
        options.Windowed           = MATCH_CLOSEST_TO_INPUT;
        options.AdapterFormat      = MATCH_CLOSEST_TO_INPUT;
        options.VertexProcessing   = MATCH_CLOSEST_TO_INPUT;
        options.Resolution         = MATCH_CLOSEST_TO_INPUT;
        options.BackBufferFormat   = MATCH_CLOSEST_TO_INPUT;
        options.BackBufferCount    = MATCH_CLOSEST_TO_INPUT;
        options.MultiSample        = MATCH_CLOSEST_TO_INPUT;
        options.SwapEffect         = MATCH_CLOSEST_TO_INPUT;
        options.DepthFormat        = MATCH_CLOSEST_TO_INPUT;
        options.StencilFormat      = MATCH_CLOSEST_TO_INPUT;
        options.PresentFlags       = MATCH_CLOSEST_TO_INPUT;
        options.RefreshRate        = MATCH_CLOSEST_TO_INPUT;
        options.PresentInterval    = MATCH_CLOSEST_TO_INPUT;

		_DeviceSettings final , optimal;
		BuildOptimalSettings( &settings , &optimal , &options );
		if( !FindValidSettings( &settings , &final , &optimal , &options ) )
		{
			TRACE_ERROR( _S("CDevice9::Initialize: Failed to find valid settings for Direct3D.") );
			FATAL_ABORT( _S("No valid device settings for requirement.") );
		}

		TRACE_INFO( _S("Found valid settings %i X %i %s %s") ,
			final.swapChainDesc.BufferDesc.Width , final.swapChainDesc.BufferDesc.Height ,
			dxgi_format_to_string( final.swapChainDesc.BufferDesc.Format ).c_str() ,
			d3d10_drvtype_to_string( final.driverType ).c_str() );

		// change device mode
		if( FAILED( ChangeMode( &settings ) ) )
		{
			// clean up
			return false;
		}

		// set mm period
		::timeBeginPeriod(1);

		return true;
	}

	// finalize
	void CDevice10::Finalize( void )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("device") );

		// lock operation
		LockGuard<ThreadLock> __lockGuard( m_lock );

		CleanupEnvironment( true );

		m_dxgiFactory->Release();
		m_dxgiFactory = NULL;

		EnumD3D10::instance().Finalize();

		::timeBeginPeriod(0);
	}

	// tick
	void CDevice10::Tick( const Frame& frame )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("tick") );

		if( m_d3dDevice == NULL )
			return;

		m_insideDeviceCallback = true;
		for( size_t i = 0 , total = m_callbacks.size() ; i < total ; ++i )
		{
			// Call the app's tick callback
			m_callbacks[i]->Tick( frame );
		}
		m_insideDeviceCallback = false;
	}

	// EnterSizing
	void CDevice10::EnterSizing( void )
	{
		m_ignoreSizeChange = true;
	}

	// ExitSizing
	void CDevice10::ExitSizing( void )
	{
		m_ignoreSizeChange = false;
		if( !m_minimized )
			CheckForSizeChange();
	}

	// on size
	void CDevice10::OnSize( bool minized )
	{
		if( m_ignoreSizeChange || !m_deviceCreated || !is_windowed( &m_currentSettings ) )
			return;

		// lock operation
		LockGuard<ThreadLock> __lockGuard( m_lock );

		RECT rcCurrentClient;
		::GetClientRect( m_hWnd , &rcCurrentClient );

        // check for DXGI buffer change
		if( !minized )
		{
			CheckForSizeChange();
		}
		m_minimized = minized;
	}

	// GetResolution
	void CDevice10::GetResolution( int& xres , int& yres )
	{
		xres = m_currentSettings.swapChainDesc.BufferDesc.Width;
		yres = m_currentSettings.swapChainDesc.BufferDesc.Height;
	}

	// CheckForSizeChange
	void CDevice10::CheckForSizeChange( void )
	{
		if( !m_releasingSwapChain )
		{
			RECT rcCurrentClient;
			::GetClientRect( m_hWnd , &rcCurrentClient );
			if( (UINT)rcCurrentClient.right != m_currentSettings.swapChainDesc.BufferDesc.Width ||
				(UINT)rcCurrentClient.bottom != m_currentSettings.swapChainDesc.BufferDesc.Height  )
			{
				// Determine if we're fullscreen
				BOOL bFullScreen;
				m_swapChain->GetFullscreenState( &bFullScreen, NULL );
				ResizeDXGIBuffers( 0, 0, bFullScreen ? true : false );
				
				::ShowWindow( m_hWnd, SW_SHOW );
			}
		}
	}
	// ResizeDXGIBuffers
	void CDevice10::ResizeDXGIBuffers( uint width , uint height , bool fullscreen )
	{
		HRESULT hr = S_OK;

		RECT rcCurrentClient;
		::GetClientRect( m_hWnd, &rcCurrentClient );

		// Determine if we're fullscreen
		m_currentSettings.swapChainDesc.Windowed = !fullscreen;

		// Call releasing
		m_insideDeviceCallback = true;
		for( size_t i = 0 , total = m_callbacks.size() ; i < total ; ++i )
		{
			bool res = m_callbacks[i]->OnResizingSwapchain( this );
			// handle result
			if( !res )
			{
				FATAL_ABORT( _S("Device resizing swap chain callback failed.") );
			}
		}
		m_insideDeviceCallback = false;

		// Release our old depth stencil texture and view 
		if( m_depthStencil )
		{
			UINT rest = m_depthStencil->Release();
			if( rest > 0 )
            {
				TRACE_ERROR( _S("Invalid Depth stencil Interface reference counter (%u).") , rest );
				FATAL_ABORT( _S("Invalid Depth stencil Interface reference counter.") );
            }
			m_depthStencil = NULL;
		}
		if( m_depthStencilView )
		{
			UINT rest = m_depthStencilView->Release();
			if( rest > 0 )
            {
				TRACE_ERROR( _S("Invalid Depth stencil view Interface reference counter (%u).") , rest );
				FATAL_ABORT( _S("Invalid Depth stencil view Interface reference counter.") );
            }
			m_depthStencilView = NULL;
		}

		// Release our old render target view
		if( m_renderTargetView )
		{
			UINT rest = m_renderTargetView->Release();
			if( rest > 0 )
            {
				TRACE_ERROR( _S("Invalid Render target view Interface reference counter (%u).") , rest );
				FATAL_ABORT( _S("Invalid Render target view Interface reference counter.") );
            }
			m_renderTargetView = NULL;
		}

		// Release our rasterizer state
		if( m_rasterizerState )
		{
			UINT rest = m_rasterizerState->Release();
			if( rest > 0 )
            {
				TRACE_ERROR( _S("Invalid Rasterizer state Interface reference counter (%u).") , rest );
				FATAL_ABORT( _S("Invalid Rasterizer state Interface reference counter.") );
            }
			m_rasterizerState = NULL;
		}

		// Alternate between 0 and DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH when resizing buffers.
		// When in windowed mode, we want 0 since this allows the app to change to the desktop
		// resolution from windowed mode during alt+enter.  However, in fullscreen mode, we want
		// the ability to change display modes from the Device Settings dialog.  Therefore, we
		// want to set the DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH flag.
		UINT Flags = 0;
		if( fullscreen )
			Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

		// ResizeBuffers
		hr = m_swapChain->ResizeBuffers(
			m_currentSettings.swapChainDesc.BufferCount,
			width,
			height,
			m_currentSettings.swapChainDesc.BufferDesc.Format,
			Flags );

		m_currentSettings.swapChainDesc.BufferDesc.Width = ( UINT )rcCurrentClient.right;
		m_currentSettings.swapChainDesc.BufferDesc.Height = ( UINT )rcCurrentClient.bottom;

		// Save off backbuffer desc
		UpdateBackBufferDesc();

		TRACE_INFO( _S("Setup views") );
		// Setup the render target view and viewport
		hr = SetupViews();
		if( FAILED( hr ) )
		{
			TRACE_ERROR( _S("CDevice10::ResizeDXGIBuffers: Failed to setup views: %s.") ,
				d3d_get_err( hr ) );
			return;
		}
		TRACE_INFO( _S("Setup views") );

		// Setup cursor based on current settings (window/fullscreen mode, show cursor state, clip cursor state)
		//DXUTSetupCursor();

		// Call the app's SwapChain reset callback
		m_insideDeviceCallback = true;
		for( size_t i = 0 , total = m_callbacks.size() ; i < total ; ++i )
		{
			bool res = m_callbacks[i]->OnResizedSwapchain( this );
			// handle result
			if( !res )
			{
				FATAL_ABORT( _S("Device resizing swap chain callback failed.") );
			}
		}
		m_insideDeviceCallback = false;
		//m_deviceLost = true;
		//GetDXUTState().SetDeviceObjectsReset( true );
		//DXUTPause( false, false );
	}
	// FindValidSettings
	bool CDevice10::FindValidSettings( _DeviceSettings * prefered , _DeviceSettings * output , _DeviceSettings * optimal , const _MatchOptions * options )
	{
		// Find the best combination of:
		//      Adapter Ordinal
		//      Device Type
		//      Back Buffer Format
		//      Windowed
		// given what's available on the system and the match options combined with the device settings input.
		// This combination of settings is encapsulated by the CD3D10EnumDeviceSettingsCombo class.
		float fBestRanking = -1.0f;
		EnumDeviceSettings10 * pBestDeviceSettings = NULL;
		DXGI_MODE_DESC adapterDesktopDisplayMode;

		EnumD3D10& enum10 = EnumD3D10::instance();
		const std::vector<EnumAdapterInfo10*>& adapterList = enum10.GetAdapterInfoList();

		for( size_t iAdapter = 0 ; iAdapter < adapterList.size() ; ++iAdapter )
		{
			EnumAdapterInfo10 * pAdapterInfo = adapterList[iAdapter];

			// Get the desktop display mode of adapter
			GetAdapterDisplayMode( pAdapterInfo->m_adapterOrdinal , 0 , adapterDesktopDisplayMode );

			// Enum all the device settings combinations.  A device settings combination is 
			// a unique set of an adapter format, back buffer format, and IsWindowed.
			for( size_t iDeviceCombo = 0; iDeviceCombo < pAdapterInfo->m_deviceSettingsList.size() ; ++iDeviceCombo )
			{
				EnumDeviceSettings10 * pDeviceSettings = pAdapterInfo->m_deviceSettingsList[iDeviceCombo];

				// Skip any combo that doesn't meet the preserve match options
				{
					//---------------------
					// Adapter ordinal
					//---------------------
					if( options->AdapterOrdinal == MATCH_PRESERVE_INPUT &&
						( pDeviceSettings->m_adapterOrdinal != prefered->adapterOrdinal ) )
						continue;

					//---------------------
					// Device type
					//---------------------
					if( options->DeviceType == MATCH_PRESERVE_INPUT &&
						( pDeviceSettings->m_deviceType != prefered->driverType ) )
						continue;

					//---------------------
					// Windowed
					//---------------------
					if( options->Windowed == MATCH_PRESERVE_INPUT &&
						( pDeviceSettings->m_windowed != prefered->swapChainDesc.Windowed ) )
						continue;

					//---------------------
					// Output
					//---------------------
					if( options->Output == MATCH_PRESERVE_INPUT &&
						( pDeviceSettings->m_output != prefered->output ) )
						continue;

					//---------------------
					// Resolution
					//---------------------
					// If keep resolution then check that width and height supported by this combo
					if( options->Resolution == MATCH_PRESERVE_INPUT )
					{
						bool bFound = false;
						for( size_t i = 0 ; i < pDeviceSettings->m_outputInfo->m_displayModeList.size() ; ++i )
						{
							DXGI_MODE_DESC displayMode = pDeviceSettings->m_outputInfo->m_displayModeList[i];
							if( displayMode.Width == prefered->swapChainDesc.BufferDesc.Width &&
								displayMode.Height == prefered->swapChainDesc.BufferDesc.Height )
							{
								bFound = true;
								break;
							}
						}

						// If the width and height are not supported by this combo, return false
						if( !bFound )
							continue;
					}

					//---------------------
					// Back buffer format
					//---------------------
					if( options->BackBufferFormat == MATCH_PRESERVE_INPUT &&
						pDeviceSettings->m_backBufferFormat != prefered->swapChainDesc.BufferDesc.Format )
						continue;

					//---------------------
					// Back buffer count
					//---------------------
					// No caps for the back buffer count

					//---------------------
					// Multisample
					//---------------------
					if( options->MultiSample == MATCH_PRESERVE_INPUT )
					{
						bool bFound = false;
						for( size_t i = 0; i < pDeviceSettings->m_multiSampleCountList.size(); ++i )
						{
							UINT Count = pDeviceSettings->m_multiSampleCountList[i];
							UINT Quality = pDeviceSettings->m_multiSampleQualityList[i];

							if( Count == prefered->swapChainDesc.SampleDesc.Count &&
								Quality > prefered->swapChainDesc.SampleDesc.Quality )
							{
								bFound = true;
								break;
							}
						}

						// If multisample type/quality not supported by this combo, then return false
						if( !bFound )
							continue;
					}

					//---------------------
					// Swap effect
					//---------------------
					// No caps for swap effects

					//---------------------
					// Depth stencil 
					//---------------------
					// No caps for depth stencil

					//---------------------
					// Present flags
					//---------------------
					// No caps for the present flags

					//---------------------
					// Refresh rate
					//---------------------
					// If keep refresh rate then check that the resolution is supported by this combo
					if( options->RefreshRate == MATCH_PRESERVE_INPUT )
					{
						bool bFound = false;
						for( size_t i = 0 ; i < pDeviceSettings->m_outputInfo->m_displayModeList.size() ; ++i )
						{
							DXGI_MODE_DESC displayMode = pDeviceSettings->m_outputInfo->m_displayModeList[i];
							float fDenom1 = 1;
							float fDenom2 = 1;

							if( displayMode.RefreshRate.Denominator )
								fDenom1 = (float)displayMode.RefreshRate.Denominator;
							if( prefered->swapChainDesc.BufferDesc.RefreshRate.Denominator )
								fDenom2 = (float)prefered->swapChainDesc.BufferDesc.RefreshRate.Denominator;

							if( fabs( float( displayMode.RefreshRate.Numerator ) / fDenom1 -
								float( prefered->swapChainDesc.BufferDesc.RefreshRate.Numerator ) / fDenom2 ) < 0.1f )
							{
								bFound = true;
								break;
							}
						}

						// If refresh rate not supported by this combo, then return false
						if( !bFound )
							continue;
					}

					//---------------------
					// Present interval
					//---------------------
					// No caps for present interval
				}

				// Get a ranking number that describes how closely this device combo matches the optimal combo
				{
					float fCurRanking = 0.0f;

					// Arbitrary weights.  Gives preference to the ordinal, device type, and windowed
					const float fAdapterOrdinalWeight = 1000.0f;
					const float fAdapterOutputWeight = 500.0f;
					const float fDeviceTypeWeight = 100.0f;
					const float fWindowWeight = 10.0f;
					const float fResolutionWeight = 1.0f;
					const float fBackBufferFormatWeight = 1.0f;
					const float fMultiSampleWeight = 1.0f;
					const float fRefreshRateWeight = 1.0f;

					//---------------------
					// Adapter ordinal
					//---------------------
					if( pDeviceSettings->m_adapterOrdinal == optimal->adapterOrdinal )
						fCurRanking += fAdapterOrdinalWeight;

					//---------------------
					// Adapter ordinal
					//---------------------
					if( pDeviceSettings->m_output == optimal->output )
						fCurRanking += fAdapterOutputWeight;

					//---------------------
					// Device type
					//---------------------
					if( pDeviceSettings->m_deviceType == optimal->driverType )
						fCurRanking += fDeviceTypeWeight;
					// Slightly prefer HAL 
					if( pDeviceSettings->m_deviceType == D3DDEVTYPE_HAL )
						fCurRanking += 0.1f;

					//---------------------
					// Windowed
					//---------------------
					if( pDeviceSettings->m_windowed == optimal->swapChainDesc.Windowed )
						fCurRanking += fWindowWeight;

					//---------------------
					// Resolution
					//---------------------
					bool bResolutionFound = false;
					for( size_t idm = 0; idm < pDeviceSettings->m_outputInfo->m_displayModeList.size(); ++idm )
					{
						DXGI_MODE_DESC displayMode = pDeviceSettings->m_outputInfo->m_displayModeList[idm];
						if( displayMode.Width == optimal->swapChainDesc.BufferDesc.Width &&
							displayMode.Height == optimal->swapChainDesc.BufferDesc.Height )
							bResolutionFound = true;
					}
					if( bResolutionFound )
						fCurRanking += fResolutionWeight;

					//---------------------
					// Back buffer format
					//---------------------
					if( pDeviceSettings->m_backBufferFormat == optimal->swapChainDesc.BufferDesc.Format )
					{
						fCurRanking += fBackBufferFormatWeight;
					}
					else
					{
						int nBitDepthDelta = abs(
							( long )dxgi_get_color_bits( pDeviceSettings->m_backBufferFormat ) -
							( long )dxgi_get_color_bits( optimal->swapChainDesc.BufferDesc.Format ) );
						float fScale = __max( 0.9f - ( float )nBitDepthDelta * 0.2f, 0.0f );
						fCurRanking += fScale * fBackBufferFormatWeight;
					}

					//---------------------
					// Back buffer count
					//---------------------
					// No caps for the back buffer count

					//---------------------
					// Multisample
					//---------------------
					bool bMultiSampleFound = false;
					for( size_t i = 0; i < pDeviceSettings->m_multiSampleCountList.size(); i++ )
					{
						UINT Count = pDeviceSettings->m_multiSampleCountList[i];
						UINT Quality = pDeviceSettings->m_multiSampleQualityList[i];

						if( Count == optimal->swapChainDesc.SampleDesc.Count &&
							Quality > optimal->swapChainDesc.SampleDesc.Quality )
						{
							bMultiSampleFound = true;
							break;
						}
					}
					if( bMultiSampleFound )
						fCurRanking += fMultiSampleWeight;

					//---------------------
					// Swap effect
					//---------------------
					// No caps for swap effects

					//---------------------
					// Depth stencil 
					//---------------------
					// No caps for swap effects

					//---------------------
					// Present flags
					//---------------------
					// No caps for the present flags

					//---------------------
					// Refresh rate
					//---------------------
					bool bRefreshFound = false;
					for( size_t idm = 0 ; idm < pDeviceSettings->m_outputInfo->m_displayModeList.size(); ++idm )
					{
						DXGI_MODE_DESC displayMode = pDeviceSettings->m_outputInfo->m_displayModeList[idm];
						float fDenom1 = 1;
						float fDenom2 = 1;

						if( displayMode.RefreshRate.Denominator )
							fDenom1 = (float)displayMode.RefreshRate.Denominator;
						if( optimal->swapChainDesc.BufferDesc.RefreshRate.Denominator )
							fDenom2 = (float)optimal->swapChainDesc.BufferDesc.RefreshRate.Denominator;

						if( fabs( float( displayMode.RefreshRate.Numerator ) / fDenom1 -
							float( optimal->swapChainDesc.BufferDesc.RefreshRate.Numerator ) / fDenom2 ) < 0.1f )
							bRefreshFound = true;
					}
					if( bRefreshFound )
						fCurRanking += fRefreshRateWeight;

					//---------------------
					// Present interval
					//---------------------
					// No caps for the present flags

					// If this combo better matches the input device settings then save it
					if( fCurRanking > fBestRanking )
					{
						pBestDeviceSettings = pDeviceSettings;
						fBestRanking = fCurRanking;
					}
				} // Get a ranking number that describes how closely this device combo matches the optimal combo
			} // for every settings
		} // for every adapter
		if( pBestDeviceSettings == NULL )
			return false;

		// Using the best device settings combo found, build valid device settings taking heed of 
		// the match options and the input device settings
		{
			DXGI_MODE_DESC adapterDisplayMode;
			GetAdapterDisplayMode( pBestDeviceSettings->m_adapterOrdinal , pBestDeviceSettings->m_output , adapterDisplayMode );

			// For each setting pick the best, taking into account the match options and 
			// what's supported by the device

			//---------------------
			// Adapter Ordinal
			//---------------------
			// Just using pBestDeviceSettings->AdapterOrdinal

			//---------------------
			// Device Type
			//---------------------
			// Just using pBestDeviceSettings->DeviceType

			//---------------------
			// Windowed 
			//---------------------
			// Just using pBestDeviceSettings->Windowed

			//---------------------
			// Output
			//---------------------
			// Just using pBestDeviceSettings->Output

			//---------------------
			// Resolution
			//---------------------
			DXGI_MODE_DESC bestDisplayMode;
			if( options->Resolution == MATCH_PRESERVE_INPUT )
			{
				bestDisplayMode.Width = prefered->swapChainDesc.BufferDesc.Width;
				bestDisplayMode.Height = prefered->swapChainDesc.BufferDesc.Height;
			}
			else
			{
				DXGI_MODE_DESC displayModeIn;
				if( options->Resolution == MATCH_CLOSEST_TO_INPUT && prefered )
				{
					displayModeIn.Width = prefered->swapChainDesc.BufferDesc.Width;
					displayModeIn.Height = prefered->swapChainDesc.BufferDesc.Height;
				}
				else // if( options->eResolution == MATCH_IGNORE_INPUT )   
				{
					if( pBestDeviceSettings->m_windowed )
					{
						// The framework defaults to 640x480 for windowed
						displayModeIn.Width = 640;
						displayModeIn.Height = 480;
					}
					else
					{
						// The framework defaults to desktop resolution for fullscreen to try to avoid slow mode change
						displayModeIn.Width = adapterDisplayMode.Width;
						displayModeIn.Height = adapterDisplayMode.Height;
					}
				}

				// Find the closest valid display mode to the optimal.
				{
					DXGI_MODE_DESC resBestMode;
					memset( &resBestMode, 0 , sizeof(DXGI_MODE_DESC) );

					if( pBestDeviceSettings->m_windowed )
					{
						bestDisplayMode = displayModeIn;

						// If our client rect size is smaller than our backbuffer size, use that size.
						// This would happen when we specify a windowed resolution larger than the screen.
						MONITORINFO Info;
						Info.cbSize = sizeof( MONITORINFO );
						::GetMonitorInfo( pBestDeviceSettings->m_outputInfo->m_desc.Monitor, &Info );

						UINT Width = Info.rcWork.right - Info.rcWork.left;
						UINT Height = Info.rcWork.bottom - Info.rcWork.top;

						RECT rcClient = Info.rcWork;
						::AdjustWindowRect( &rcClient, ::GetWindowLong( m_hWnd , GWL_STYLE ), FALSE );
						Width = Width - ( rcClient.right - rcClient.left - Width );
						Height = Height - ( rcClient.bottom - rcClient.top - Height );

						bestDisplayMode.Width = __min( bestDisplayMode.Width, Width );
						bestDisplayMode.Height = __min( bestDisplayMode.Height, Height );
					}
					else
					{
						int nBestRanking = 100000;
						int nCurRanking;
						std::vector<DXGI_MODE_DESC>& displayModeList = pBestDeviceSettings->m_outputInfo->m_displayModeList;
						for( size_t iDisplayMode = 0 ; iDisplayMode < displayModeList.size() ; ++iDisplayMode )
						{
							DXGI_MODE_DESC displayMode = displayModeList[iDisplayMode];

							// Find the delta between the current width/height and the optimal width/height
							nCurRanking = abs((int)displayMode.Width - (int)displayModeIn.Width) + 
								abs((int)displayMode.Height- (int)displayModeIn.Height);

							if( nCurRanking < nBestRanking )
							{
								resBestMode = displayMode;
								nBestRanking = nCurRanking;

								// Stop if perfect match found
								if( nBestRanking == 0 )
									break;
							}
						}

						if( resBestMode.Width == 0 )
						{
							// No valid display modes found
							bestDisplayMode = displayModeIn;
						}
						else
						{
							bestDisplayMode = resBestMode;
						}
					}
				} // find valid resolution
			}

			//---------------------
			// Back Buffer Format
			//---------------------
			// Just using pBestDeviceSettings->BackBufferFormat

			//---------------------
			// Back Buffer usage
			//---------------------
			// Just using prefered->swapChainDesc.BackBufferUsage | DXGI_USAGE_RENDERTARGETOUTPUT

			//---------------------
			// Back buffer count
			//---------------------
			UINT bestBackBufferCount;
			if( options->BackBufferCount == MATCH_PRESERVE_INPUT )
			{
				bestBackBufferCount = prefered->swapChainDesc.BufferCount;
			}
			else if( options->BackBufferCount == MATCH_IGNORE_INPUT )
			{
				// The framework defaults to triple buffering 
				bestBackBufferCount = 2;
			}
			else // if( options->eBackBufferCount == MATCH_CLOSEST_TO_INPUT )   
			{
				bestBackBufferCount = prefered->swapChainDesc.BufferCount;
				if( bestBackBufferCount > 3 )
					bestBackBufferCount = 3;
				if( bestBackBufferCount < 1 )
					bestBackBufferCount = 1;
			}

			//---------------------
			// Multisample
			//---------------------
			UINT bestMultiSampleCount;
			UINT bestMultiSampleQuality;
			if( prefered && prefered->swapChainDesc.SwapEffect != DXGI_SWAP_EFFECT_DISCARD )
			{
				// Swap effect is not set to discard so multisampling has to off
				bestMultiSampleCount = 1;
				bestMultiSampleQuality = 0;
			}
			else
			{
				if( options->MultiSample == MATCH_PRESERVE_INPUT )
				{
					bestMultiSampleCount = prefered->swapChainDesc.SampleDesc.Count;
					bestMultiSampleQuality = prefered->swapChainDesc.SampleDesc.Quality;
				}
				else if( options->MultiSample == MATCH_IGNORE_INPUT )
				{
					// Default to no multisampling (always supported)
					bestMultiSampleCount = 1;
					bestMultiSampleQuality = 0;
				}
				else if( options->MultiSample == MATCH_CLOSEST_TO_INPUT )
				{
					// Default to no multisampling (always supported)
					bestMultiSampleCount = 1;
					bestMultiSampleQuality = 0;

					for( size_t i = 0; i < pBestDeviceSettings->m_multiSampleCountList.size(); ++i )
					{
						UINT Count = pBestDeviceSettings->m_multiSampleCountList[i];
						UINT Quality = pBestDeviceSettings->m_multiSampleQualityList[i];

						// Check whether supported type is closer to the input than our current best
						if( labs( Count - prefered->swapChainDesc.SampleDesc.Count ) <
							labs( bestMultiSampleCount - prefered->swapChainDesc.SampleDesc.Count ) )
						{
							bestMultiSampleCount = Count;
							bestMultiSampleQuality = __min( Quality - 1, prefered->swapChainDesc.SampleDesc.Quality );
						}
					}
				}
				else
				{
					// Error case
					bestMultiSampleCount = 1;
					bestMultiSampleQuality = 0;
				}
			}

			//---------------------
			// Swap effect
			//---------------------
			DXGI_SWAP_EFFECT bestSwapEffect;
			if( options->SwapEffect == MATCH_PRESERVE_INPUT )
			{
				bestSwapEffect = prefered->swapChainDesc.SwapEffect;
			}
			else if( options->SwapEffect == MATCH_IGNORE_INPUT )
			{
				bestSwapEffect = DXGI_SWAP_EFFECT_DISCARD;
			}
			else // if( options->eSwapEffect == MATCH_CLOSEST_TO_INPUT )
			{
				bestSwapEffect = prefered->swapChainDesc.SwapEffect;

				// Swap effect has to be one of these 2
				if( bestSwapEffect != DXGI_SWAP_EFFECT_DISCARD &&
					bestSwapEffect != DXGI_SWAP_EFFECT_SEQUENTIAL )
				{
					bestSwapEffect = DXGI_SWAP_EFFECT_DISCARD;
				}
			}

			//---------------------
			// Depth stencil 
			//---------------------
			DXGI_FORMAT bestDepthStencilFormat;
			bool bestEnableAutoDepthStencil;

			if( options->DepthFormat == MATCH_IGNORE_INPUT &&
				options->StencilFormat == MATCH_IGNORE_INPUT )
			{
				bestEnableAutoDepthStencil = true;
				bestDepthStencilFormat = DXGI_FORMAT_D32_FLOAT;
			}
			else
			{
				bestEnableAutoDepthStencil = prefered->autoCreateDepthStencil ? true : false;
				bestDepthStencilFormat = prefered->autoDepthStencilFormat;
			}

			//---------------------
			// Present flags
			//---------------------

			//---------------------
			// Refresh rate
			//---------------------
			if( pBestDeviceSettings->m_windowed )
			{
				// Must be 0 for windowed
				bestDisplayMode.RefreshRate.Numerator = 0;
				bestDisplayMode.RefreshRate.Denominator = 0;
			}
			else
			{
				if( options->RefreshRate == MATCH_PRESERVE_INPUT )
				{
					bestDisplayMode.RefreshRate = prefered->swapChainDesc.BufferDesc.RefreshRate;
				}
				else
				{
					DXGI_RATIONAL refreshRateMatch;
					if( options->RefreshRate == MATCH_CLOSEST_TO_INPUT )
					{
						refreshRateMatch = prefered->swapChainDesc.BufferDesc.RefreshRate;
					}
					else // if( options->eRefreshRate == MATCH_IGNORE_INPUT )
					{
						refreshRateMatch = adapterDisplayMode.RefreshRate;
					}

					// Default to 0 in case no matching mode is found.
					// 0, in this case means that we'll let DXGI choose the best one for us
					bestDisplayMode.RefreshRate.Numerator = 0;
					bestDisplayMode.RefreshRate.Denominator = 0;

					// if( refreshRateMatch != 0 )
					{
						float fBestRefreshRanking = 100000.0f;
						std::vector<DXGI_MODE_DESC>& displayModeList = pBestDeviceSettings->m_outputInfo->m_displayModeList;
						for( size_t iDisplayMode = 0 ; iDisplayMode < displayModeList.size() ; ++iDisplayMode )
						{
							DXGI_MODE_DESC displayMode = displayModeList[iDisplayMode];
							if( displayMode.Height != bestDisplayMode.Height ||
								displayMode.Width != bestDisplayMode.Width )
								continue; // Skip display modes that don't match

							// Find the delta between the current refresh rate and the optimal refresh rate
							float fDenom1 = 1;
							float fDenom2 = 1;
							if( displayMode.RefreshRate.Denominator )
								fDenom1 = (float)displayMode.RefreshRate.Denominator;
							if( refreshRateMatch.Denominator )
								fDenom2 = (float)refreshRateMatch.Denominator;

							float fCurRanking = abs( float( displayMode.RefreshRate.Numerator ) / fDenom1  -
													 float( refreshRateMatch.Numerator ) / fDenom2 );

							if( fCurRanking < fBestRefreshRanking )
							{
								bestDisplayMode.RefreshRate = displayMode.RefreshRate;
								fBestRefreshRanking = fCurRanking;

								// Stop if good-enough match found
								if( fBestRefreshRanking < 0.1f )
									break;
							}
						}
					}
				}
			} // refresh rate

			//---------------------
			// Present interval
			//---------------------
			UINT32 bestPresentInterval;
			if( options->PresentInterval == MATCH_PRESERVE_INPUT )
			{
				bestPresentInterval = prefered->syncInterval;
			}
			else if( options->PresentInterval == MATCH_IGNORE_INPUT )
			{
				// For windowed and fullscreen, default to 1 which will wait for
				// the vertical retrace period to prevent tearing. For benchmarking,
				// use 0 which will will wait not for the vertical retrace period
				// but may introduce tearing.

				// The reference driver does not support v-syncing and will
				// produce _com_error exceptions when the sync interval is
				// anything but 0.
				bestPresentInterval = ( D3D10_DRIVER_TYPE_REFERENCE == pBestDeviceSettings->m_deviceType ) ? 0 : 1;
			}
			else // if( options->ePresentInterval == MATCH_CLOSEST_TO_INPUT )
			{
				bestPresentInterval = prefered->syncInterval;
			}

			// Fill the device settings struct
			ZeroMemory( output , sizeof( _DeviceSettings ) );
			output->adapterOrdinal							= pBestDeviceSettings->m_adapterOrdinal;
			output->output									= pBestDeviceSettings->m_output;
			output->driverType								= pBestDeviceSettings->m_deviceType;
			output->swapChainDesc.BufferDesc.Width			= bestDisplayMode.Width;
			output->swapChainDesc.BufferDesc.Height			= bestDisplayMode.Height;
			output->swapChainDesc.BufferDesc.Format			= pBestDeviceSettings->m_backBufferFormat;
			output->swapChainDesc.BufferUsage 				= prefered->swapChainDesc.BufferUsage | DXGI_USAGE_RENDER_TARGET_OUTPUT;
			output->swapChainDesc.BufferCount 				= bestBackBufferCount;
			output->swapChainDesc.SampleDesc.Count			= bestMultiSampleCount;
			output->swapChainDesc.SampleDesc.Quality 		= bestMultiSampleQuality;
			output->swapChainDesc.SwapEffect				= bestSwapEffect;
			output->swapChainDesc.OutputWindow				= m_hWnd;
			output->swapChainDesc.Windowed					= pBestDeviceSettings->m_windowed;
			output->swapChainDesc.BufferDesc.RefreshRate	= bestDisplayMode.RefreshRate;
			output->swapChainDesc.Flags						= 0;
			output->syncInterval							= bestPresentInterval;
			output->autoCreateDepthStencil 					= bestEnableAutoDepthStencil;
			output->autoDepthStencilFormat 					= bestDepthStencilFormat;
			output->createFlags								= prefered->createFlags;
		}
		return true;
	}
	// BuildOptimalSettings
	void CDevice10::BuildOptimalSettings( _DeviceSettings * prefered , _DeviceSettings * optimal , const _MatchOptions * options )
	{
		ZeroMemory( optimal, sizeof( _DeviceSettings ) );

		// Retrieve the desktop display mode.
		DXGI_MODE_DESC adapterDesktopDisplayMode = { 800, 600, { 0, 0 }, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB };
		GetAdapterDisplayMode( prefered->adapterOrdinal , 0 , adapterDesktopDisplayMode );

		//---------------------
		// Adapter ordinal
		//---------------------
		if( options->AdapterOrdinal == MATCH_IGNORE_INPUT )
			optimal->adapterOrdinal = 0;
		else
			optimal->adapterOrdinal = prefered->adapterOrdinal;

		//---------------------
		// Device type
		//---------------------
		if( options->DeviceType == MATCH_IGNORE_INPUT )
			optimal->driverType = D3D10_DRIVER_TYPE_HARDWARE;
		else
			optimal->driverType = prefered->driverType;

		//---------------------
		// Windowed
		//---------------------
		if( options->Windowed == MATCH_IGNORE_INPUT )
			optimal->swapChainDesc.Windowed = TRUE;
		else
			optimal->swapChainDesc.Windowed = prefered->swapChainDesc.Windowed;

		//---------------------
		// Output #
		//---------------------
		if( options->Output == MATCH_IGNORE_INPUT )
			optimal->output = 0;
		else
			optimal->output = prefered->output;

		//---------------------
		// Create flags
		//---------------------
		optimal->createFlags = prefered->createFlags;

		//---------------------
		// Resolution
		//---------------------
		if( options->Resolution == MATCH_IGNORE_INPUT )
		{
			// If windowed, default to 640x480
			// If fullscreen, default to the desktop res for quick mode change
			if( optimal->swapChainDesc.Windowed )
			{
				optimal->swapChainDesc.BufferDesc.Width = 640;
				optimal->swapChainDesc.BufferDesc.Height = 480;
			}
			else
			{
				optimal->swapChainDesc.BufferDesc.Width = adapterDesktopDisplayMode.Width;
				optimal->swapChainDesc.BufferDesc.Height = adapterDesktopDisplayMode.Height;
			}
		}
		else
		{
			optimal->swapChainDesc.BufferDesc.Width = prefered->swapChainDesc.BufferDesc.Width;
			optimal->swapChainDesc.BufferDesc.Height = prefered->swapChainDesc.BufferDesc.Height;
		}

		//---------------------
		// Back buffer format
		//---------------------
		if( options->BackBufferFormat == MATCH_IGNORE_INPUT )
			optimal->swapChainDesc.BufferDesc.Format = adapterDesktopDisplayMode.Format; // Default to match the adapter format
		else
			optimal->swapChainDesc.BufferDesc.Format = prefered->swapChainDesc.BufferDesc.Format;

		//---------------------
		// Back buffer usage
		//---------------------
		optimal->swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;

		//---------------------
		// Back buffer count
		//---------------------
		if( options->BackBufferCount == MATCH_IGNORE_INPUT )
			optimal->swapChainDesc.BufferCount = 2; // Default to triple buffering for perf gain
		else
			optimal->swapChainDesc.BufferCount = prefered->swapChainDesc.BufferCount;

		//---------------------
		// Multisample
		//---------------------
		if( options->MultiSample == MATCH_IGNORE_INPUT )
		{
			// Default to no multisampling 
			optimal->swapChainDesc.SampleDesc.Count = 0;
			optimal->swapChainDesc.SampleDesc.Quality = 0;
		}
		else
		{
			optimal->swapChainDesc.SampleDesc.Count = prefered->swapChainDesc.SampleDesc.Count;
			optimal->swapChainDesc.SampleDesc.Quality = prefered->swapChainDesc.SampleDesc.Quality;
		}

		//---------------------
		// Swap effect
		//---------------------
		if( options->SwapEffect == MATCH_IGNORE_INPUT )
			optimal->swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
		else
			optimal->swapChainDesc.SwapEffect = prefered->swapChainDesc.SwapEffect;

		//---------------------
		// Depth stencil 
		//---------------------
		if( options->DepthFormat == MATCH_IGNORE_INPUT &&
			options->StencilFormat == MATCH_IGNORE_INPUT )
		{
			optimal->autoCreateDepthStencil = TRUE;
			optimal->autoDepthStencilFormat = DXGI_FORMAT_D32_FLOAT;
		}
		else
		{
			optimal->autoCreateDepthStencil = prefered->autoCreateDepthStencil;
			optimal->autoDepthStencilFormat = prefered->autoDepthStencilFormat;
		}

		//---------------------
		// Present flags
		//---------------------
		if( options->PresentFlags == MATCH_IGNORE_INPUT )
			optimal->presentFlags = 0;
		else
			optimal->presentFlags = prefered->presentFlags;

		//---------------------
		// Refresh rate
		//---------------------
		if( options->RefreshRate == MATCH_IGNORE_INPUT )
		{
			optimal->swapChainDesc.BufferDesc.RefreshRate.Numerator = 0;
			optimal->swapChainDesc.BufferDesc.RefreshRate.Denominator = 0;
		}
		else
			optimal->swapChainDesc.BufferDesc.RefreshRate = prefered->swapChainDesc.BufferDesc.RefreshRate;

		//---------------------
		// Present interval
		//---------------------
		if( options->PresentInterval == MATCH_IGNORE_INPUT )
		{
			// For windowed and fullscreen, default to 1 which will
			// wait for the vertical retrace period to prevent tearing.
			// For benchmarking, use 0 which will not wait for the
			// vertical retrace period but may introduce tearing.
			optimal->syncInterval = 1;
		}
		else
		{
			optimal->syncInterval = prefered->syncInterval;
		}
	}
	// GetAdapterDisplayMode
	void CDevice10::GetAdapterDisplayMode( UINT AdapterOrdinal, UINT nOutput, DXGI_MODE_DESC& pModeDesc )
	{
		const EnumOutputInfo10 * pOutputInfo = EnumD3D10::instance().GetOutputInfo( AdapterOrdinal , nOutput );
		if( pOutputInfo )
		{
			pModeDesc.Width = 800;
			pModeDesc.Height = 600;
			pModeDesc.RefreshRate.Numerator = 0;
			pModeDesc.RefreshRate.Denominator = 0;
			pModeDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
			pModeDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
			pModeDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;

			DXGI_OUTPUT_DESC Desc;
			pOutputInfo->m_pOutput->GetDesc( &Desc );
			pModeDesc.Width = Desc.DesktopCoordinates.right - Desc.DesktopCoordinates.left;
			pModeDesc.Height = Desc.DesktopCoordinates.bottom - Desc.DesktopCoordinates.top;
		}

		// TODO: verify this is needed
		if( pModeDesc.Format == DXGI_FORMAT_B8G8R8A8_UNORM )
			pModeDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	}
	// GetBackBufferSize
	Pair<int> CDevice10::GetBackBufferSize( void )
	{
		return Pair<int>( m_backBufferSurfaceDesc.Width , m_backBufferSurfaceDesc.Height );
	}
	
} // namespace xeres

